/**
 * 
 */
package com.xohm.properties;

import java.io.IOException;
import java.lang.reflect.*;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

/**
 * Xohm Connection Manager
 *
 * Licensed Material - Property of Sprint Nextel
 * Restricted Materials of Sprint Nextel
 * (C) Copyright Sprint Nextel Corp. 2008 - All Rights Reserved.
 *
 * This class handles the parsing of the configuration file and sets attributes
 * in predefined classes in the XML document.  Reflection is used to set the
 * attributes.  This class also will read the config.xml file, get the values
 * from the attributes in the classes and save it back to the xml file.<br><br>
 * 
 * The config file should look something like:<br><br>
 * 
 * <ConnectionManagerConfiguration><br>
 *	<Properties class="com.xohm.base.Properties"><br>
 *		<Attribute name="debug" type="boolean" value="false"/><br>
 *		<Attribute name="numberOfLogs" type="int" value="10"/><br>
 *	</Properties><br>
 *	<Properties class="com.xohm.session.Properties"><br>
 *		<Attribute name="logStatisticsEvery" type="int" value="30"/><br>
 *		<Attribute name="checkConnectedEvery" type="int" value="50"/><br>
 *	</Properties><br>
 * </ConnectionManagerConfiguration><br><br>
 *
 * @author Robin Katzer
 */
public class ConfigXMLParserHandler extends DefaultHandler
{
	private boolean reading         = true;  // If false then we're writing
	private String elementValue     = null;
	private Object property         = null;
	public static StringBuffer updatedXml = null;
	
	// Element Names
	public static final String CM_CONFIG  = "ConnectionManagerConfiguration";
	public static final String PROPERTIES = "Properties";
	public static final String ATTRIBUTE  = "Attribute";
	
	// Attribute Names
	public static final String CLASS = "class";
	public static final String NAME  = "name";
	public static final String TYPE  = "type";
	public static final String VALUE = "value";
	

	public ConfigXMLParserHandler()
	{
	}

	public static void parseConfigXML(InputSource xmlInputSource, boolean readAttributes)
	{
		// Instance of this class is the handler.
		ConfigXMLParserHandler handler = new ConfigXMLParserHandler();
		handler.reading = readAttributes;
		if (!readAttributes)
			updatedXml = new StringBuffer(2000);

		//get a factory
		SAXParserFactory spf = SAXParserFactory.newInstance();
		try
		{
			//get a new instance of parser
			spf.setValidating(false);
			SAXParser sp = spf.newSAXParser();
			
			// parse the file and also register this class for call backs
			sp.parse(xmlInputSource, handler);
		
		}catch(SAXException se) {
			se.printStackTrace();
		}catch(ParserConfigurationException pce) {
			pce.printStackTrace();
		}catch (IOException ie) {
			ie.printStackTrace();
		}
	}
	
	public void startDocument() throws SAXException
	{
		// If we're writting then start the updated XML doc
		if (!reading)
			updatedXml.append("<ConnectionManagerConfiguration>\r\n");
	}
	
	public void startElement(String uri, String localName, String qName, Attributes atts)
		throws SAXException
	{
		if (qName.equalsIgnoreCase(PROPERTIES))
		{
			try
			{
				String classString = atts.getValue(CLASS);
				property = Class.forName(classString).newInstance();
				
				if (!reading)
					updatedXml.append("  <Properties class=\"").append(classString).append("\">\r\n");
			}
			catch (Exception e)
			{
				throw new SAXException(e.toString());
			}
		}
		else
		if (qName.equalsIgnoreCase(ATTRIBUTE))
		{
			try
			{
				String attrName  = atts.getValue(NAME);
				String attrType  = atts.getValue(TYPE);
				String attrValue = atts.getValue(VALUE);

				if (reading)
				{
					Class c = property.getClass();
		         Field field = c.getDeclaredField(attrName);
		         setField(field, attrType, attrValue);
				}
				else
				{
					Class c = property.getClass();
		         Field field = c.getDeclaredField(attrName);
		         attrValue = String.valueOf(field.get(property));

		         updatedXml.append("    <Attribute name=\"")
					          .append(attrName)
					          .append("\" type=\"")
					          .append(attrType)
					          .append("\" value=\"")
					          .append(attrValue)
					          .append("\"/>\r\n");
				}
			}
			catch (Exception e)
			{
				throw new SAXException(e.toString());
			}
		}
	}
	
	private void setField(Field field, String attrType, String attrValue) throws SAXException
	{
		try
		{
			if (attrType.equalsIgnoreCase("byte"))
			{
				field.setByte(property, Byte.valueOf(attrValue).byteValue());
			} else
			if (attrType.equalsIgnoreCase("short"))
			{
				field.setShort(property, Short.valueOf(attrValue).shortValue());
			} else
			if (attrType.equalsIgnoreCase("int"))
			{
				field.setInt(property, Integer.valueOf(attrValue).intValue());
			} else
			if (attrType.equalsIgnoreCase("long"))
			{
				field.setLong(property, Long.valueOf(attrValue).longValue());
			} else
			if (attrType.equalsIgnoreCase("float"))
			{
				field.setFloat(property, Float.valueOf(attrValue).floatValue());
			} else
			if (attrType.equalsIgnoreCase("double"))
			{
				field.setDouble(property, Double.valueOf(attrValue).doubleValue());
			} else
			if (attrType.equalsIgnoreCase("boolean"))
			{
				if (attrValue.equalsIgnoreCase("true"))
					field.setBoolean(property, true);
				else
					field.setBoolean(property, false);
			} else
			if (attrType.equalsIgnoreCase("char"))
			{
				field.setChar(property, attrValue.toCharArray()[0]);
			}
			else
			{
				field.set(property, attrValue);
			}
		}
		catch (Exception e)
		{
			throw new SAXException(e.toString());
		}
	}
	
	public void endElement(String uri, String localName, String qName) throws SAXException
	{
		if (qName.equalsIgnoreCase(PROPERTIES))
		{
			property = null;

			if (!reading)
				updatedXml.append("  </Properties>\r\n");
		}
	}
	
	/**
	 * End of XML document - Nothing to do. 
	 * 
	 * @throws org.xml.sax.SAXException
	 */
	public void endDocument() throws SAXException
	{
		// Save the document with the new values
		if (!reading)
		{
			updatedXml.append("</ConnectionManagerConfiguration>\r\n");
		}
	}
	
	public void characters(char ch[], int start, int length) throws SAXException
	{
		if (elementValue == null)
			elementValue = new String(ch,start,length).trim();
		else
			elementValue = elementValue + new String(ch,start,length).trim();
	}
}
